package com.huawei.spare.part.replace.models;

import com.fasterxml.jackson.annotation.JsonIgnore;
import com.google.common.base.Strings;
import com.huawei.spare.part.replace.framework.SmartDataBus;
import com.huawei.spare.part.replace.utils.PatternUtils;
import java.io.File;
import java.io.IOException;
import java.io.Serializable;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import javafx.scene.image.WritableImage;
import lombok.Data;
import lombok.ToString;
import net.schmizz.sshj.SSHClient;
import net.schmizz.sshj.common.IOUtils;
import net.schmizz.sshj.connection.channel.direct.Session;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import static com.huawei.spare.part.replace.Constants.BoardTypePatter;
import static com.huawei.spare.part.replace.Constants.CpldVersionPatter;
import static com.huawei.spare.part.replace.Constants.GET_FRU_VERSION;
import static com.huawei.spare.part.replace.Constants.SWITCH_IS_PRESENCE;
import static com.huawei.spare.part.replace.Constants.SlotNames;
import static com.huawei.spare.part.replace.Constants.SoftwareVersionPatter;

/**
 * Replacement model represent all attributes required by a "replacement" project
 */
@Data
@ToString
public class Replacement implements Serializable {

  File projectFile;
  Project project;
  SmmAuth smmAuth = new SmmAuth();
  SwitchAuth switchAuth;
  String runPcIp;         // 运行工具的IP地址

  Slot slot;

  @JsonIgnore
  List<Uplink> uplinks;

  @JsonIgnore
  WritableImage uplinkTableSnapshot;

  /**
   * get switch export config file path
   * <p>
   * configs
   * |- ${board_type}-swi${slot}-080910.tar.gz
   */
  public String getExportConfigFilePath() {
    final String parent = projectFile.getParentFile().getAbsolutePath();
    final String filename = String.format("%s-swi%d-%s.tar.gz",
        project.getDefective().getBoardType(), slot.getPos(), new SimpleDateFormat("yyyyMMdd").format(new Date()));
    final Path configFolder = Paths.get(parent, "configs");
    if (!new File(configFolder.toString()).exists()) {
      new File(configFolder.toString()).mkdirs();
    }
    final Path configFile = configFolder.resolve(filename);
    return configFile.toAbsolutePath().toString();
  }

  @Data
  @ToString
  public static class SmmAuth implements Serializable {
    String smmHost;
    String smmUsername;
    String smmPassword;
    String solUsername;
    String solPassword;
  }

  @Data
  @ToString
  public static class SwitchAuth implements Serializable {
    String host;
    String mask;
    String username;
    String password;
    String ubootPassword;
  }

  @Data
  @ToString
  public static class Slot implements Serializable {

    private static final Logger log = LoggerFactory.getLogger(Slot.class);

    Integer pos;                // slot position 1-4
    boolean isPresent;            // is switch present in this slot
    String boardType;
    String cpldVersion;
    String softwareVersion;
    boolean isIdentified = false;

    public Slot() {
    }

    public Slot(Integer pos) {
      this.pos = pos;
    }

    public String getDisplayMessage() {
      String boardType = isPresent ? StringUtils.defaultIfBlank(this.boardType, "slot.unknown") : "slot.not.used";
      final String slotName = SlotNames.get(this.getPos() - 1);
      final String slotBoardType = SmartDataBus.getString(boardType);
      if (isPresent) {
        final String message = SmartDataBus.getString("slot.selected.slot.present");
        return String.format(message, slotName, slotBoardType);
      } else {
        final String message = SmartDataBus.getString("slot.selected.slot.not.present");
        return String.format(message, slotName);
      }
    }

    public String getIsSwitchPresentCmd() {
      return String.format(SWITCH_IS_PRESENCE, pos);
    }

    public String getFruVersionCmd(int fruId) {
      return String.format(GET_FRU_VERSION, pos, fruId);
    }

    public Slot read(final SSHClient sshClient) throws IOException {
      isPresent = isPresent(sshClient);
      if (isPresent) {
        if (!readVersion(sshClient, 2)) {
          readVersion(sshClient, 1);
        }
      }
      return this;
    }

    public boolean isPresent(SSHClient sshClient) throws IOException {
      try (Session session = sshClient.startSession()) {
        final String isSwitchPresentCmd = getIsSwitchPresentCmd();
        Session.Command command = session.exec(isSwitchPresentCmd);
        String isSlotPresenceResult = IOUtils.readFully(command.getInputStream()).toString();
        log.info("{} -> {}", isSwitchPresentCmd, isSlotPresenceResult);
        this.isPresent = isSlotPresenceResult.indexOf("is present") >= 0;
        return this.isPresent;
      }
    }

    private boolean readVersion(SSHClient sshClient, int fruId) throws IOException {
      try (Session session = sshClient.startSession()) {
        final String getFRUVersionCmd = getFruVersionCmd(fruId);
        Session.Command command = session.exec(getFRUVersionCmd);
        String getFRUVersionResult = IOUtils.readFully(command.getInputStream()).toString();
        log.info("{} -> {}", getFRUVersionCmd, getFRUVersionResult);
        if (getFRUVersionResult.indexOf("does not support the function") >= 0
            || getFRUVersionResult.indexOf("The command cannot be executed") >= 0) {
          return false;
        }

        /**
         swi2:fru2 Version Information:
         StartupTime 2018/08/07   09:54:14
         Memory      Size    : 2048 M bytes
         Flash       Size    : 1024 M bytes
         CX110 version information
         1. PCB      Version : CX110
         2. MAB      Version : 0
         3. Board    Type    : CX110
         4. CPLD1    Version : 004
         5. BIOS     Version : 2.43
         6. Software Version : 2.10
         */
        if (getFRUVersionResult.contains("version information")) {
          boardType = PatternUtils.getMatchedContent(getFRUVersionResult, BoardTypePatter, 1);
          if (!Strings.isNullOrEmpty(boardType)) {
            boardType = boardType.split("_")[0];
          }
          log.info("Detect board type from command result: {}", boardType);
          cpldVersion = PatternUtils.getMatchedContent(getFRUVersionResult, CpldVersionPatter, 1);
          cpldVersion = cpldVersion != null ? "V" + cpldVersion : null;
          log.info("Detect CPLD version from command result: {}", cpldVersion);
          softwareVersion = PatternUtils.getMatchedContent(getFRUVersionResult, SoftwareVersionPatter, 1);
          softwareVersion = softwareVersion != null ? "V" + softwareVersion : null;
          log.info("Detect software version from command result: {}", softwareVersion);
          this.isIdentified = true;
          return true;
        }

        return false;
      }
    }
  }
}
